﻿Imports System.Configuration
Imports System.Reflection


''' <summary>
''' Custom Rules
''' </summary>
''' <remarks>
''' These rules will be loaded from supplied DLL file. All rules should implement IRule(Of String) Interface.
''' </remarks>
Public Class CustomRule
    Inherits ConfigurationElement
    Implements IRule(Of String)

    Private Parameters As New Dictionary(Of String, String)

    ''' <summary>
    ''' Gets a value indicating whether an unknown attribute is encountered during deserialization.
    ''' </summary>
    ''' <param name="name">The name of the unrecognized attribute.</param>
    ''' <param name="value">The value of the unrecognized attribute.</param>
    ''' <returns>
    ''' true when an unknown attribute is encountered while deserializing; otherwise, false.
    ''' </returns>
    Protected Overrides Function OnDeserializeUnrecognizedAttribute(ByVal name As String, ByVal value As String) As Boolean
        Me.Parameters.Add(name, value)
        Return True
    End Function

    ''' <summary>
    ''' Gets or sets the name of the rule. This name will be used to reference this rule.
    ''' </summary>
    ''' <value>The name.</value>
    <ConfigurationProperty("name")> _
    Public Property Name() As String
        Get
            Return DirectCast(Me.Item("name"), String)
        End Get
        Set(ByVal Value As String)
            Me.Item("name") = Value
        End Set
    End Property

    ''' <summary>
    ''' Gets the name of the function to call.
    ''' </summary>
    ''' <value>The name of the function to call.</value>
    Private ReadOnly Property ClassToCall() As String
        Get
            Return NetBouncer.GetInstance().CustomRuleDefinitions(Me.Name).Type
        End Get
    End Property

    ''' <summary>
    ''' Determines whether the specified input is valid.
    ''' </summary>
    ''' <param name="input">The input.</param>
    ''' <returns>
    ''' <c>true</c> if the specified input is valid; otherwise, <c>false</c>.
    ''' </returns>
    Public Function IsValid(ByVal input As String) As Boolean Implements IRule(Of String).IsValid

        Dim AssemblyName As String = String.Empty
        Dim Type As String = String.Empty

        'Parse FQ Type Name
        If Not ParseType(AssemblyName, Type, Me.ClassToCall) Then
            Throw New ArgumentException("Not correctly referenced type")
            Return False
        End If

        'Load Assembly
        Dim CustomAssembly As Assembly = Assembly.Load(AssemblyName)
        Dim CustomType As Type = CustomAssembly.GetType(Type, True, False)

        Dim TypeArr(Me.Parameters.Count - 1) As Type
        For TypeIndex As Integer = 0 To TypeArr.Length - 1
            TypeArr(TypeIndex) = GetType(Object)
        Next

        'Get a new instance of Custom Rule
        Dim RawCustomRule As Object = Activator.CreateInstance(CustomType, New Object() {})

        'Set properties
        For Each Param As KeyValuePair(Of String, String) In Me.Parameters
            Dim CustomProperty As PropertyInfo = CustomType.GetProperty(Param.Key)
            CustomProperty.SetValue(RawCustomRule, Convert.ChangeType(Param.Value, CustomProperty.PropertyType), Nothing)
        Next

        'Cast and Return
        Dim CustomRule As IRule(Of String) = DirectCast(RawCustomRule, IRule(Of String))

        Return CustomRule.IsValid(input)
    End Function


    ''' <summary>
    ''' Parses the assembly and type.
    ''' </summary>
    ''' <param name="assembly">The assembly.</param>
    ''' <param name="type">The type.</param>
    ''' <param name="input">The input.</param>
    ''' <returns></returns>
    Private Shared Function ParseType(ByRef assembly As String, ByRef type As String, ByVal input As String) As Boolean
        Const seperator As String = ","

        Dim Pos As Integer = input.IndexOf(seperator)
        If Pos = -1 Then Return False

        type = input.Substring(0, Pos).Trim()
        assembly = input.Substring(Pos + 1, input.Length - Pos - 1).Trim()

        Return True
    End Function

End Class

